package cdu.sl.service.impl;

import cdu.sl.common.utils.BaseContext;
import cdu.sl.common.utils.JwtUtils;
import cdu.sl.common.utils.PasswordEncoder;
import cdu.sl.domain.dto.LoginByVerCodeFormDTO;
import cdu.sl.domain.dto.LoginFormDTO;
import cdu.sl.domain.dto.RegisterFormDTO;
import cdu.sl.domain.dto.UserUpdateDTO;
import cdu.sl.domain.po.User;
import cdu.sl.domain.vo.LoginVO;
import cdu.sl.domain.vo.UserInfoVO;
import cdu.sl.service.EmailService;
import cdu.sl.service.UserService;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cdu.sl.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final PasswordEncoder passwordEncoder;

    private final EmailService emailService;

    private final JwtUtils jwtUtils;

    private final RedisTemplate redisTemplate;

    private final UserMapper userMapper;

    @Override
    public void register(RegisterFormDTO registerFormDTO) {
        // 1.先根据用户名判断用户是否已经存在
        String username = registerFormDTO.getUsername();
        String email = registerFormDTO.getEmail();

        // 检查用户名是否已存在
        User existUser = lambdaQuery().eq(User::getUsername, username).one();
        Assert.isNull(existUser, "用户名已存在");

        // 检查邮箱是否已存在
        existUser = lambdaQuery().eq(User::getEmail, email).one();
        Assert.isNull(existUser, "邮箱已被注册");

        // 2.对密码进行加密
        String encPassword = passwordEncoder.encode(registerFormDTO.getPassword());

        // 3.保存用户，存储加密后的密码
        userMapper.insert(User.builder()
                .username(registerFormDTO.getUsername())
                .email(registerFormDTO.getEmail())
                .password(encPassword)
                .isActive(true)
                .build());

    }

    @Override
    public LoginVO login(LoginFormDTO loginFormDTO) {
        // 1.根据用户名查询用户
        String username = loginFormDTO.getUsername();

        User user = lambdaQuery().eq(User::getUsername, username).one();
        // 2.用户不存在 直接抛出异常
        Assert.notNull(user, "用户不存在！");
        log.info("查询到用户数据：{}", user);

        // 3.用户存在 校验密码
        if (!passwordEncoder.matches(loginFormDTO.getPassword(), user.getPassword())) {
            throw new RuntimeException("密码错误！");
        }

        // 4.更新登录时间
        LocalDateTime now = LocalDateTime.now();
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("username", username)
                .set("last_login", now);
        update(updateWrapper);

        // 5.生成token
        String token = jwtUtils.generateToken(user.getId(), loginFormDTO.getRememberMe());

        // 6.构建并返回登录响应信息
        LoginVO loginVO = new LoginVO();
        BeanUtil.copyProperties(user, loginVO);
        loginVO.setToken(token);

        return loginVO;
    }

    /**
     * 向目标用户发送验证码
     *
     * @param emailAddress 邮箱地址
     * @return
     */
    @Override
    public String sendVerificationCode(String emailAddress) {
        // 1.首先检查邮箱地址是否已经注册
        User user = lambdaQuery().eq(User::getEmail, emailAddress).one();
        Assert.notNull(user, "用户不存在！");
        log.info("用户：{} 获取验证码", user);

        // 2. 向目标邮箱发送验证码
        String code = emailService.sendVerificationCode(emailAddress);

        // 3.将用户验证码保存到缓存中 , 过期时间为五分钟
        String key = "user:" + user.getId();
        redisTemplate.opsForValue().set(key, code, 300, TimeUnit.SECONDS);

        return code;
    }


    /**
     * 验证码登录
     *
     * @param loginByVerCodeFormDTO 验证码登录表单实体
     * @return
     */
    @Override
    public LoginVO loginByVerCode(LoginByVerCodeFormDTO loginByVerCodeFormDTO) {
        // 1.检查邮箱是否已经注册
        String email = loginByVerCodeFormDTO.getEmail();
        User user = lambdaQuery().eq(User::getEmail, email).one();
        Assert.notNull(user, "邮箱未注册,请先注册！");

        // 2.从缓存中获取验证码
        String key = "user:" + user.getId();
        String code = (String) redisTemplate.opsForValue().get(key);
        System.out.println(code);
        // 3.校验验证码
        if (!Objects.equals(code, loginByVerCodeFormDTO.getVerificationCode())) {
            throw new RuntimeException("验证码错误！");
        }


        // 4.更新登录时间
        LocalDateTime now = LocalDateTime.now();
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("email", email)
                .set("last_login", now);
        update(updateWrapper);

        // 5.响应用户数据和token
        String token = jwtUtils.generateToken(user.getId(), loginByVerCodeFormDTO.getRememberMe());

        LoginVO loginVO = new LoginVO();
        loginVO.setLoginTime(LocalDateTime.now());
        loginVO.setToken(token);
        BeanUtil.copyProperties(user, loginVO);
        return loginVO;
    }

    /**
     * 获取当前用户信息（从token解析）
     *
     * @return 当前用户信息
     */
    @Override
    @Cacheable(cacheNames = "userCache", key = "T(cdu.sl.common.utils.BaseContext).getCurrentId()")
    public UserInfoVO getCurrentUserInfo() {
        // 从BaseContext中获取userId
        Long userId=BaseContext.getCurrentId();
        Assert.notNull(userId, "无效的认证令牌");
        
        // 3. 根据用户ID查询用户信息
        User user = getById(userId);
        Assert.notNull(user, "用户不存在");
        
        // 4. 转换为VO对象并返回
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtil.copyProperties(user, userInfoVO);
        
        return userInfoVO;
    }

    /**
     * 修改用户信息
     *
     * @param userUpdateDTO 用户更新信息
     * @return 更新后的用户信息
     */
    @Override
    @Transactional
    @CacheEvict(cacheNames = "userCache", key = "T(cdu.sl.common.utils.BaseContext).getCurrentId()")
    public UserInfoVO updateUser(UserUpdateDTO userUpdateDTO) {
        // 1. 检查用户是否存在
        Long userId = BaseContext.getCurrentId();
        User user = getById(userId);
        Assert.notNull(user, "用户不存在");
        
        // 2. 检查用户名是否已被其他用户使用
        if (StringUtils.hasText(userUpdateDTO.getUsername()) && !userUpdateDTO.getUsername().equals(user.getUsername())) {
            // 检查用户名是否已被其他用户使用
            User existUser = lambdaQuery().eq(User::getUsername, userUpdateDTO.getUsername()).one();
            Assert.isNull(existUser, "用户名已被使用");
        }

        // 3. 更新用户信息
        // 创建更新对象
        User updateUser = new User();
        updateUser.setId(userId);
        UpdateWrapper<User> updateWrapper=new UpdateWrapper<>();
        updateWrapper.eq("id",userId);
        updateWrapper.set("username",userUpdateDTO.getUsername());
        updateWrapper.set("account_type",userUpdateDTO.getAccountType());
        updateWrapper.set("avatar",userUpdateDTO.getAvatar());
        userMapper.update(updateUser,updateWrapper);


        // 4. 查询更新后的用户信息并返回
        User updatedUser = getById(userId);
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtil.copyProperties(updatedUser, userInfoVO);
        
        return userInfoVO;
    }


}

